package methodset

import (
	"fmt"
)

// Animal 动物接口
type Animal interface {
	Speak() string
	Move() string
}

// Dog 狗结构体
type Dog struct {
	Name string
}

// Speak 实现Animal接口的Speak方法
func (d Dog) Speak() string {
	return fmt.Sprintf("%s说: 汪汪汪!", d.Name)
}

// Move 实现Animal接口的Move方法
func (d Dog) Move() string {
	return fmt.Sprintf("%s跑: 快快快!", d.Name)
}

// Eat 狗的特有方法
func (d Dog) Eat() string {
	return fmt.Sprintf("%s吃: 骨头!", d.Name)
}

// Cat 猫结构体
type Cat struct {
	Name string
}

// Speak 实现Animal接口的Speak方法
func (c *Cat) Speak() string {
	return fmt.Sprintf("%s说: 喵喵喵!", c.Name)
}

// Move 实现Animal接口的Move方法
func (c *Cat) Move() string {
	return fmt.Sprintf("%s跑: 悄悄悄!", c.Name)
}

// DemoMethodSet 演示方法集的概念
func DemoMethodSet() {
	fmt.Println("=== 方法集演示 ===")

	// 方法集规则:
	// 1. 类型T的方法集包含所有接收者为T的方法
	// 2. 类型*T的方法集包含所有接收者为T和*T的方法
	// 3. 接口的方法集是接口中声明的方法

	// Dog类型的方法集
	dog := Dog{Name: "小黑"}
	fmt.Println("Dog类型实例:")
	fmt.Println(dog.Speak()) // 可以调用，接收者为Dog
	fmt.Println(dog.Move())  // 可以调用，接收者为Dog
	fmt.Println(dog.Eat())   // 可以调用，接收者为Dog

	// Dog指针类型的方法集
	dogPtr := &Dog{Name: "小白"}
	fmt.Println("\n*Dog类型实例:")
	fmt.Println(dogPtr.Speak()) // 可以调用，接收者为Dog
	fmt.Println(dogPtr.Move())  // 可以调用，接收者为Dog
	fmt.Println(dogPtr.Eat())   // 可以调用，接收者为Dog

	// Cat类型的方法集
	cat := Cat{Name: "咪咪"}
	fmt.Println("\nCat类型实例:")
	cat.Speak() // 不能调用，因为Speak方法的接收者是*Cat
	cat.Move()  // 不能调用，因为Move方法的接收者是*Cat
	fmt.Println("注意: Cat类型实例不能调用Speak和Move方法，因为这些方法的接收者是*Cat")

	// Cat指针类型的方法集
	catPtr := &Cat{Name: "花花"}
	fmt.Println("\n*Cat类型实例:")
	fmt.Println(catPtr.Speak()) // 可以调用，接收者为*Cat
	fmt.Println(catPtr.Move())  // 可以调用，接收者为*Cat

	// 接口实现
	fmt.Println("\n接口实现:")
	// Dog类型实例可以赋值给Animal接口，因为Dog实现了所有Animal接口的方法
	var animal1 Animal = dog
	fmt.Println(animal1.Speak())
	fmt.Println(animal1.Move())

	// *Dog类型实例也可以赋值给Animal接口
	var animal2 Animal = dogPtr
	fmt.Println(animal2.Speak())
	fmt.Println(animal2.Move())

	// Cat类型实例不能赋值给Animal接口，因为Cat没有实现Animal接口的方法
	// var animal3 Animal = cat // 编译错误

	// *Cat类型实例可以赋值给Animal接口，因为*Cat实现了所有Animal接口的方法
	var animal4 Animal = catPtr
	fmt.Println(animal4.Speak())
	fmt.Println(animal4.Move())
}
